{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c151c246-e70b-4365-87f2-f9c18facd9c3",
   "metadata": {},
   "source": [
    "# Simon's Algorithm\n",
    "\n",
    "The Simon's algorithm [[1](#SimonsWiki)] is one of the basic quantum algorithms that demonstrates an exponential speed-up over its classical counterpart, in the oracle complexity setting. The algorithm solves the so called Simon's problem: \n",
    "\n",
    "* **Input:** A function $f: [0,1]^N \\rightarrow [0,1]^N$.\n",
    "\n",
    "* **Promise:**  There is a secret binary string $s$ such that\n",
    "$$\n",
    "f(x) = f(y) \\iff x\\oplus  y \\in \\{0, s\\},\n",
    "\\tag{1}\n",
    "$$\n",
    "where $\\oplus$ is a bitwise xor operation.\n",
    "\n",
    "* **Output:** The secret string $s$, using a minimal number of queries of $f$.\n",
    "\n",
    "*** \n",
    "\n",
    "\n",
    "Note that the condition on $f$ implies that it is 2-to-1 if $s \\neq 0^N$, and 1-to-1 otherwise. Herefter we refer to a function $f(x)$ that satisfies the condition in Eq. ([1](#mjx-eqn-1)) as a \"Simon's function\".\n",
    "\n",
    "Problem hardeness: The Simon's problem is hard to solve with a classical, deterministic or probabalistic, approaches. This can be understood as follows: determining $s$ requries finding a collision $f(x)=f(y)$, as $s = x\\oplus y$. What is the minimal number of calls for measuring a collision? If we take the deterministic approach, in the worst case we will need $2^{N-1}$ calls. A probablistic approach, in the spirit of the one that solves the Birthday problem [[2](#BDWiki)], has a slightly better scaling of $O(2^{N/2})$ queries.\n",
    "\n",
    "**The quantum approach requires $O(N)$ queries, thus, introducing an exponential speedup**.\n",
    "\n",
    "Next, we define the Simon's algorithm, which has a [quantum part](#The-Quantum-Part) and a [classical postprocess part](#The-Classical-Postprocess). Then, we run the algorithm on two different examples of a Simon's function: one that can be defined with [simple arithmetic](#Example:-Arithmetic-Simon's-Function), and another that has a [shallow implementation](#Example:-Shallow-Simon's-Function). A [mathematical explanation](#Technical-Notes) of the algorithm is provided at the end of this notebook."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df99d31d-588f-4292-9c6e-328dc75bd10a",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"https://docs.classiq.io/resources/simons_algorithm.png\" style=\"width:80%\">\n",
    "<figcaption align = \"middle\"> Figure 1. The Simon's algorithm is comprised of two quantum blocks. The main part of the algorithm\n",
    "is the oracle which implements the Simon's function f(x). </figcaption>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "082e2c2c-3e14-41d7-91b8-eaea042faac7",
   "metadata": {},
   "source": [
    "## How to Build the Algorithm with Classiq"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29bafcd3-7bf8-4c29-82ab-0df8104683c0",
   "metadata": {},
   "source": [
    "### The Quantum Part\n",
    "\n",
    "The quantum part of the algorithm is rather simple, calling the quantum implementation of $f(x)$, between two calls of the hadamard transform. The call of $f$ is done out-of-place, onto a quantum variable $y$, whereas only the final state of $x$ is relevant to the classical post-process to follow. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e4307bea-48a6-4561-9325-dcc60f3e5e52",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:04.513790Z",
     "iopub.status.busy": "2024-05-07T14:48:04.513297Z",
     "iopub.status.idle": "2024-05-07T14:48:07.284978Z",
     "shell.execute_reply": "2024-05-07T14:48:07.284234Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import Output, QCallable, QNum, allocate_num, hadamard_transform, qfunc\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def simon_qfunc(f_qfunc: QCallable[QNum, Output[QNum]], x: QNum):\n",
    "\n",
    "    res = QNum(\"res\")\n",
    "    hadamard_transform(x)\n",
    "    f_qfunc(x, res)\n",
    "    hadamard_transform(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9bb1577-fb51-43b8-8e81-ea8aed88dd14",
   "metadata": {},
   "source": [
    "### The Classical Postprocess\n",
    "\n",
    "The classical part of the algorithm includes the following post-processing steps:\n",
    "1. Finding $N-1$ samples of $x$ that are linearly independent, $\\{y_k\\}^{n-1}_{1}$. It is gurenteed that this can be acheived with high probability, see the [technical details](#The-classical-part) below.\n",
    "2. Finding the string $s$ such that $s \\cdot y_k=0 \\,\\,\\, \\forall k$, where $\\cdot$ refers to a dot-product mod 2 (polynomial complexity in $N$).\n",
    "\n",
    "For these steps we use the *Galois* package, which extends *numpy* to finite field operations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "ec2fe11a-5ecf-44cf-9f2c-b8530bf1ad3a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:07.290106Z",
     "iopub.status.busy": "2024-05-07T14:48:07.288609Z",
     "iopub.status.idle": "2024-05-07T14:48:07.293547Z",
     "shell.execute_reply": "2024-05-07T14:48:07.292945Z"
    }
   },
   "outputs": [],
   "source": [
    "# !pip install galois"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2f3f89eb-dca1-410d-a4d1-441bd89cd11b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:07.297573Z",
     "iopub.status.busy": "2024-05-07T14:48:07.296517Z",
     "iopub.status.idle": "2024-05-07T14:48:10.434951Z",
     "shell.execute_reply": "2024-05-07T14:48:10.434360Z"
    }
   },
   "outputs": [],
   "source": [
    "import galois\n",
    "import numpy as np\n",
    "\n",
    "# here we work over boolean arithmetics - F(2)\n",
    "GF = galois.GF(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80a47118-75de-4128-9260-c4ee2aba3e31",
   "metadata": {},
   "source": [
    "We define two classical functions for the first step: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "53bb09e7-3907-4ec6-b4d5-e4c6f34e9d8d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:10.439194Z",
     "iopub.status.busy": "2024-05-07T14:48:10.438242Z",
     "iopub.status.idle": "2024-05-07T14:48:10.444058Z",
     "shell.execute_reply": "2024-05-07T14:48:10.443506Z"
    }
   },
   "outputs": [],
   "source": [
    "# The following function checks whether a set contains linearly independet vectors\n",
    "\n",
    "\n",
    "def is_independent_set(vectors):\n",
    "    matrix = GF(vectors)\n",
    "    rank = np.linalg.matrix_rank(matrix)\n",
    "    if rank == len(vectors):\n",
    "        return True\n",
    "    else:\n",
    "        return False\n",
    "\n",
    "\n",
    "def get_independent_set(samples):\n",
    "    \"\"\"\n",
    "    The following function gets samples of n-sized strings from running the quantum part and return an n-1 x n matrix,\n",
    "    whose rows forms a set if independent\n",
    "    \"\"\"\n",
    "    ind_v = []\n",
    "    for v in samples:\n",
    "        if is_independent_set(ind_v + [v]):\n",
    "            ind_v.append(v)\n",
    "            if len(ind_v) == len(v) - 1:\n",
    "                # reached max set of N-1\n",
    "                break\n",
    "    return ind_v"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ffe2431-6dea-41fc-9125-00e5ab329ffd",
   "metadata": {},
   "source": [
    "For the second step we simply need to solve a linear set of equations. We have $N-1$ equations on a binary vector of size $N$.\n",
    "It has two solutions, one of them is the trivial solution $0^N$, and the other gives us the secret string $s$. The *Galois* package handles this task as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e01d7967-0520-45d1-adda-57d7f300e0c0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:10.446364Z",
     "iopub.status.busy": "2024-05-07T14:48:10.446186Z",
     "iopub.status.idle": "2024-05-07T14:48:10.449640Z",
     "shell.execute_reply": "2024-05-07T14:48:10.449027Z"
    }
   },
   "outputs": [],
   "source": [
    "def get_secret_integer(matrix):\n",
    "\n",
    "    gf_v = GF(matrix)  # converting to a matrix over Z_2\n",
    "    null_space = gf_v.T.left_null_space()  # finding the right-null space of the matrix\n",
    "    return int(\n",
    "        \"\".join(np.array(null_space)[0][::-1].astype(str)), 2\n",
    "    )  # converting from binary to integer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7355697-73b4-4821-bba1-bc7f0b0abf19",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Next we provide two different examples of Simon's function, and run the Simon's algorithm to find their secret string.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6ba27a3-29b8-4c87-87fe-a1a87c19ca83",
   "metadata": {},
   "source": [
    "## Example: Arithmetic Simon's Function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76a43559-06bd-42be-af16-a85d2d80b422",
   "metadata": {},
   "source": [
    "An example of a valid $f(x)$ function that satisfies the condition in Eq. ([1](#mjx-eqn-1)) is:\n",
    "$$\n",
    "f(x) = \\min(x, x\\oplus s).\n",
    "$$\n",
    "Clearly, we have that $f(x\\oplus s) = \\min(x\\oplus s, (x\\oplus s)\\oplus s)=\\min(x\\oplus s, x)=f(x)$."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f457af6a-897d-4aa1-a3aa-86aa49530b1c",
   "metadata": {},
   "source": [
    "### Implementing of the Simon's Function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eead5314-6008-426e-9a4e-92365c3e53c6",
   "metadata": {},
   "source": [
    "We define the function, as well as a model that applies it on all computational basis states to illustrate that it is a two-to-one function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "4a353a36-5da0-4a2d-a44a-1c08669ee616",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:10.451942Z",
     "iopub.status.busy": "2024-05-07T14:48:10.451768Z",
     "iopub.status.idle": "2024-05-07T14:48:10.455133Z",
     "shell.execute_reply": "2024-05-07T14:48:10.454528Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import CInt, Output, QNum, create_model, hadamard_transform, qfunc\n",
    "from classiq.qmod.symbolic import min\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def simon_qfunc_simple(s: CInt, x: QNum, res: Output[QNum]):\n",
    "    res |= min(x, x ^ s)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7a128c2-6c61-4639-9716-9c0a93184023",
   "metadata": {},
   "source": [
    "Let us run it with $N=5$ and $s={'}00110{'} (\\equiv 6)$, starting with a uniform distribution of $|x\\rangle$ over all possible states:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8ebfa471-7675-4a97-9655-8475495ac55b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:10.457422Z",
     "iopub.status.busy": "2024-05-07T14:48:10.457052Z",
     "iopub.status.idle": "2024-05-07T14:48:17.406429Z",
     "shell.execute_reply": "2024-05-07T14:48:17.405785Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/61cffecc-7617-4bb7-b554-4ec59239f1ae?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "from classiq import Constraints, allocate, execute, show, synthesize\n",
    "\n",
    "NUM_QUBITS = 5\n",
    "S_SECRET = 6\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(x: Output[QNum], res: Output[QNum]):\n",
    "    allocate(NUM_QUBITS, x)\n",
    "    hadamard_transform(x)\n",
    "    simon_qfunc_simple(S_SECRET, x, res)\n",
    "\n",
    "\n",
    "qmod = create_model(main, constraints=Constraints(optimization_parameter=\"width\"))\n",
    "\n",
    "# synthesize\n",
    "qprog = synthesize(qmod)\n",
    "\n",
    "# vizualize\n",
    "show(qprog)\n",
    "\n",
    "# execute\n",
    "result = execute(qprog).result()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e8c6ff9-580c-4577-9e83-fe3c60761b08",
   "metadata": {},
   "source": [
    "By plotting the results we can see that this is two-to-one function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "2f0662ee-ed60-4504-ba2c-30ba1bca8e3e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:17.409478Z",
     "iopub.status.busy": "2024-05-07T14:48:17.408936Z",
     "iopub.status.idle": "2024-05-07T14:48:17.951846Z",
     "shell.execute_reply": "2024-05-07T14:48:17.950842Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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mVbWkkmmlbXMSnMQZqUBAOBczEozCbPSyHTjesl0V6bT1kumuzLtul5mpZmMWWlkml1LJplW1pJJppW1zEoDAACwARojAAAAPxojAAAAPxojAAAAPxojAAAAPxojAAAAPxojAAAAPxojAAAAPxojAAAAP+583UoMkSXTrFoyybSylkwyraxliKyDMEQWAADnYoisSRgiS6Yb10umuzKdtl4y3ZVp1/UyRNZkDJEl0+xaMsm0spZMMq2sZYgsAACADdAYAQAA+Nm+MTp27JjWr1+vu+66S4MGDVJCQoLatGmj1NRUXX755VqzZk2T9W+//bbGjh2r5ORktW/fXuecc47mzJmj7777LkLvAAAAOIXtG6MNGzbokksu0cMPP6zS0lINGzZM//Vf/6XTTjtNubm5uuyyy3TjjTeqoc+QP/bYYxo9erTWrl2r8847T+PGjVNlZaUWLFiggQMHqry83IJ3BAAA7Mr2jVFMTIzGjx+vjRs3qqysTG+88YZeeuklffLJJ3rxxRcVGxurP/3pT1q+fHmduu3bt+vOO+9UbGys1qxZow0bNuivf/2rPv/8c40aNUq7du3STTfdZNG7AgAAdmT7xmjkyJF65ZVXNHz48HrPXX311Zo6daokadmyZXWeu//++2UYhq677jpdeumlgcdPPfVULV26VDExMXr11Vf16aefmrp+AADgHLZvjJqTkZEhSdq7d2/gsaNHjwY+ezRx4sR6NT169NDQoUMlSatWrYrAKgEAgBM4vjHas2ePJCktLS3w2O7du3Xo0CFJ0sCBAxusq318+/btJq8QAAA4haNv8Lhv3z4999xzkqTx48cHHi8pKZEkJSQkKD4+vsHa9PT0Oq9tTHV1taqrqwM/+3w+ScxKI9O8WjLJtLKWTDKtrGVWWgi+//57ZWVlaf369erXr58KCgrUtm1bSdLKlSs1adIkde3aVaWlpQ3W//nPf9YNN9ygs88+W7t27Wo0Jzs7W/Pmzav3OLPSAABwjpbOSnPsGaObbrpJ69evV+fOnfXKK68EmqJwmzVrlqZPnx742efzKT09XWPGjGFWGpmuWS+Z7sp02nrJdFemXddbe8WnOY5sjG677TYtXbpUiYmJWrdunc4+++w6z9dePjt48GCjv6P2Bo9NdY2SFBcXp7i4uHqPMyuNTLNrySTTyloyybSylllpQbjzzju1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4+LiFBcXV+9xZqWRaXYtmWRaWUsmmVbWWjkrLeTG6KuvvtL69etVVFSkr776St9++60SExPVpUsXnX/++Ro5cqS6dOkSakxYJSUlKSUlRWVlZSotLbV6OQAAwCZa1RgdO3ZML730knJycrRt2zZJUkNX5DyeE3cwHTx4sH73u9/pqquuanUHGE41NTWqrKyUJL6dBgAAAoJujJYvX65Zs2aprKxMhmHotNNO05AhQ3Teeeepc+fO6tSpkyorK7V//37t2LFDW7du1QcffKD8/HzNnDlT999/vyZPnmzGe2mx1atX69ChQ/J4PBo4cKClawEAAPYRVGM0ZMgQbdu2TcnJybr11ls1depU9e/fv9m6jz76SM8++6z+8pe/6Ne//rWeeOIJbdmypdWLbs6XX36pjRs3asKECWrXru7k5tdeey1wJ+1JkyYpNTXVtHUAAABnCaox2rNnjx588EHdcsstDX4guTEDBgzQokWL9OCDD2rx4sV64IEHglpkUVGRbr755sDPn3/+uSTpqaeeCnz1XpJWrVqltLQ0VVRUaMqUKZo2bZoyMjLUtWtXHT58WMXFxdqzZ48kacSIEVqyZElQ6wAAAO4WVGP0j3/8o8mvuDUnLi5Od911l2688cag6nw+n/Lz8+s9XlpaWufD07Vfq09PT9eMGTP04Ycf6rPPPlNRUZGOHj2q5ORkXXbZZZo4caKuvvpqZq4BAIA6gmqMftwUVVZWyuv1Bh0abHN18cUXN/jh7sZ07txZCxcuDHZZAAAgyoV0ymTEiBHav39/uNYCAABgqZAao48++kg/+9nPtG/fvmZf25q5JwAAAJEU0g0e7777bj344IMaPny41q9fr+7duzf4updeekmzZ88OfGjaDRgiS6ZZtWSSaWUtmWRaWeuKIbL333+/5syZo/T0dL399ts666yzAs998MEHmj59euCD0zU1NaFEWSonJ0c5OTmqqanR7t27GSILAICDRHSI7JIlS3TLLbcoJSVFeXl5io+P14wZM/TKK6/IMAz16NFD8+fPt/zGjuHg8/nk9XoZIkumq9ZLprsynbZeMt2Vadf1RnSI7LRp09SpUyddd911Gj58eGAifVJSkmbPnq1bbrmlyQn2TsQQWTLNriWTTCtrySTTylpHD5GVpOPHj+vgwYOKj4/Xt99+K4/Ho2uuuUZLlixp1df5AQAArBDyHQ5XrVqlvn37atq0afr222914YUXSpLefvttlZSUhLxAAACASAmpMbrwwgs1YcIEffrpp8rIyNC7776rzZs36+mnn9a3336rkSNH6v333w/XWgEAAEwVUmP0wQcfqGvXrnr++edVUFCgiy66SJJ03XXXaeXKlTp48KD+8z//U+vWrQvLYgEAAMwUUmM0f/587d69W1OmTKn33C9/+UutWrVKx48f1+WXX65Vq1aFEgUAAGC6kBqjOXPmqF27do0+P3bsWL311ltq06aNrrnmmlCiAAAATGf6ePmLLrpIb7/9tuLj482OAgAACInpjZEkXXDBBXrvvfciEQUAANBqYbmPUUv07ds3UlERwaw0Ms2qJZNMK2vJJNPKWsfNSrvtttv0v//7v+rcuXNLS+r55ptvNH/+fC1evLjVv8MKzEoDAMC5TJmVFhsbqw4dOuh3v/udfvOb39QZGNucXbt26emnn9ZTTz2lw4cPt6obtANmpZHpxvWS6a5Mp62XTHdl2nW9psxKKyws1C233KIHHnhADz74oIYMGaJRo0ZpyJAhOvfcc9W5c2d17NhR3333nfbv36/i4mJt3bpV69at07Zt22QYhoYOHarHH388qDdrR8xKI9PsWjLJtLKWTDKtrHXMrLS//OUveuCBB1RWVqbHHntMW7Zs0ZYtW+TxeBqtqT0hdeGFF+qOO+7Q+PHjg4kEAACImKAao4ceekjl5eVaunSpJkyYoIKCAr3xxht65513tH37dh08eDDw2g4dOigzM1MjRozQFVdcoQEDBoR77QAAAGEVVGMUGxurmpqawM+DBw/WzTffrI0bN0o68cGmyspKJSQkqH379uFdKQAAgMmCaoySkpJUWloa+NkwjDpniU499VS+qQUAABwrqBs8ZmZmasOGDXrqqadUXV1t1poAAAAsEVRjdPfdd0uSbr75ZiUmJsrj8aiwsFBPP/20ioqKHPsVfAAAACnIS2kjRozQO++8o/nz52vDhg2SpE8++UQ33nijpBNfhevbt6/OP//8wF8/+clPWv2VOwAAgEgKeiTI8OHDlZeXp8OHD6tDhw664IILNHDgQBUUFOj//u//VFRUpKKiIj399NOSTjRL5513ns4//3z96U9/CvsbAAAACJdWz0qr/dZZnz599Mc//lGSVFNTo7///e8qLCxUYWFhoFnavn27Pvro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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "my_result = {\n",
    "    sample.state[\"x\"]: sample.state[\"res\"] for sample in result[0].value.parsed_counts\n",
    "}\n",
    "fig, ax = plt.subplots()\n",
    "ax.plot(my_result.keys(), my_result.values(), \"o\")\n",
    "ax.grid(axis=\"y\", which=\"minor\")\n",
    "ax.grid(axis=\"y\", which=\"major\")\n",
    "ax.grid(axis=\"x\", which=\"minor\")\n",
    "ax.grid(axis=\"x\", which=\"major\")\n",
    "plt.xlabel(\"$x$\", fontsize=16)\n",
    "plt.ylabel(\"$f(x)$\", fontsize=16)\n",
    "plt.yticks(fontsize=16)\n",
    "plt.xticks(fontsize=16)\n",
    "ax.minorticks_on()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e20bf877-78c2-49e8-8e0d-d8af7b2919d8",
   "metadata": {},
   "source": [
    "### Running the Simon's Algorithm\n",
    "\n",
    "Taking $N$ number of shots gurentees getting a set of $N-1$ independet strings with high probability (assuming a noiseless quantum computer), see [technical explanation](#The-quantum-part) below. Moreover, increasing the number of shots by a constant factor provides an exponential improvment. Below we take $50*N$ shots."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "ee24ec02-1eef-4ae1-9905-d9722570146e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:17.961126Z",
     "iopub.status.busy": "2024-05-07T14:48:17.959565Z",
     "iopub.status.idle": "2024-05-07T14:48:17.992767Z",
     "shell.execute_reply": "2024-05-07T14:48:17.991839Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import allocate_num\n",
    "from classiq.execution import ExecutionPreferences\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(x: Output[QNum]):\n",
    "\n",
    "    allocate(NUM_QUBITS, x)\n",
    "    simon_qfunc(lambda x, res: simon_qfunc_simple(S_SECRET, x, res), x)\n",
    "\n",
    "\n",
    "qmod = create_model(\n",
    "    main,\n",
    "    constraints=Constraints(optimization_parameter=\"width\"),\n",
    "    execution_preferences=ExecutionPreferences(num_shots=50 * NUM_QUBITS),\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5f809285-c1c5-440c-b494-641781b95b27",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:17.998362Z",
     "iopub.status.busy": "2024-05-07T14:48:17.996687Z",
     "iopub.status.idle": "2024-05-07T14:48:18.018090Z",
     "shell.execute_reply": "2024-05-07T14:48:18.017305Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import write_qmod\n",
    "\n",
    "write_qmod(qmod, \"simon_example\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe7d80e1-875a-494e-8fa2-ffead27178fa",
   "metadata": {},
   "source": [
    "We Synthesize and execute to obtain the results "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "1223b440-0939-444f-8042-bcae991a443d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:18.023200Z",
     "iopub.status.busy": "2024-05-07T14:48:18.022015Z",
     "iopub.status.idle": "2024-05-07T14:48:24.755464Z",
     "shell.execute_reply": "2024-05-07T14:48:24.754716Z"
    }
   },
   "outputs": [],
   "source": [
    "qprog = synthesize(qmod)\n",
    "result = execute(qprog).result()\n",
    "samples = [\n",
    "    [int(k) for k in key] for key in result[0].value.counts_of_output(\"x\").keys()\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "8d1bf53a-7906-4ab6-b40b-061ac38a560b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:24.758795Z",
     "iopub.status.busy": "2024-05-07T14:48:24.758243Z",
     "iopub.status.idle": "2024-05-07T14:48:24.852862Z",
     "shell.execute_reply": "2024-05-07T14:48:24.852185Z"
    }
   },
   "outputs": [],
   "source": [
    "matrix_of_ind_v = get_independent_set(samples)\n",
    "assert (\n",
    "    len(matrix_of_ind_v) == NUM_QUBITS - 1\n",
    "), \"Failed to find an independent set, try to increase the number of shots\"\n",
    "quantum_secret_integer = get_secret_integer(matrix_of_ind_v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "3af79a3a-a37b-416c-9580-02392b45305f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:24.855688Z",
     "iopub.status.busy": "2024-05-07T14:48:24.855370Z",
     "iopub.status.idle": "2024-05-07T14:48:24.859074Z",
     "shell.execute_reply": "2024-05-07T14:48:24.858437Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The secret binary string (integer) of f(x): 6\n",
      "The result of the Simon's Algorithm: 6\n"
     ]
    }
   ],
   "source": [
    "print(\"The secret binary string (integer) of f(x):\", S_SECRET)\n",
    "print(\"The result of the Simon's Algorithm:\", quantum_secret_integer)\n",
    "assert (\n",
    "    S_SECRET == quantum_secret_integer\n",
    "), \"The Simon's algorithm failed to find the secret key.\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78c84b07-cd4d-466b-8f99-13629b149c03",
   "metadata": {},
   "source": [
    "## Example: Shallow Simon's Function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b3887fc-fa53-46be-a519-e911bb54781a",
   "metadata": {},
   "source": [
    "In the second example we take a Simon's function that was presented in a recent paper [[3](#SimonsPaper2024)]: Take a secret string of the form $s=0^{N-L}1^L = \\underbrace{00\\dots0}_{N-L}\\underbrace{1\\dots111}_{L}$, and define the 2-to-1 function:\n",
    "$$\n",
    "f_{s}(|x\\rangle_N) = \\underbrace{|x_0\\rangle |x_1\\rangle \\dots |x_{N-L-1}\\rangle}_{N-L} |0\\rangle \\underbrace{|x_{N-L+1}\\oplus x_{N-L}\\rangle \\dots |x_{N-1}\\oplus x_{N-L}\\rangle}_{L-1}.\n",
    "$$\n",
    "The function $f$ operates as follows: for the first $N-L$ elements we simply \"copy\" the data, whereas for the last $L$ elements we apply a xor with the $N-L$ element. A simple proof that this is indeed a 2-to-1 function is given in Ref. [[3](#SimonsPaper2024)].\n",
    "\n",
    "*Comment:* Ref. [[3](#SimonsPaper2024)] employed further reduction of the function implementation (reducing the $N$-sized Simon's problem to an $(N-L)$-sized problem), added a classical post-process of randomly permutating over the result of $f(x)$ to increase the hardness of the problem, as well as included some NISQ analysis. These steps where taken to show an algorithmic speedup on real quantum hardware."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65a1970a-ec4e-40ca-89f1-cb341d70b08a",
   "metadata": {},
   "source": [
    "### Implementing of the Simon's Function\n",
    "\n",
    "The first $N-L$ \"classical copies\", $|x_k,0\\rangle\\rightarrow |x_k x_k\\rangle$, can be implemented by $CX$ gates. The xor operations, $|x_k,0\\rangle\\rightarrow |x_k, x_k \\oplus x_{N-L}\\rangle$, can be implemented by two CX operations, one for a \"classical copy\" of $x_k$, followed by a $CX$ operation to apply a xor with $x_{N-L}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "718afafc-a0ed-49c3-a793-e8cba7eac6c1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:24.861699Z",
     "iopub.status.busy": "2024-05-07T14:48:24.861256Z",
     "iopub.status.idle": "2024-05-07T14:48:24.865718Z",
     "shell.execute_reply": "2024-05-07T14:48:24.865158Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import CX, QArray, QBit, repeat\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def simon_qfunc_with_bipartite_s(\n",
    "    partition_index: CInt, x: QArray[QBit], res: Output[QArray[QBit]]\n",
    "):\n",
    "\n",
    "    allocate(x.len, res)\n",
    "\n",
    "    repeat(x.len - partition_index, lambda i: CX(x[i], res[i]))\n",
    "    repeat(\n",
    "        partition_index - 1,\n",
    "        lambda i: (\n",
    "            CX(\n",
    "                x[x.len - partition_index + 1 + i],\n",
    "                res[x.len - partition_index + 1 + i],\n",
    "            ),\n",
    "            CX(x[x.len - partition_index], res[x.len - partition_index + 1 + i]),\n",
    "        ),\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82f3f088-cb2c-4539-a29a-37e8d3d894b0",
   "metadata": {},
   "source": [
    "Below we take a specific example, and plot $f(x)$ for all possible $x$ values: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "7024f574-2ea3-4864-aa14-8155466a11a4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:24.868151Z",
     "iopub.status.busy": "2024-05-07T14:48:24.867723Z",
     "iopub.status.idle": "2024-05-07T14:48:28.922759Z",
     "shell.execute_reply": "2024-05-07T14:48:28.921998Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/c9184a7a-af13-4a42-9fa9-c7457ce35f89?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "NUM_QUBITS = 6\n",
    "PARTITION_INDEX = 4\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(x: Output[QNum], res: Output[QNum]):\n",
    "    allocate(NUM_QUBITS, x)\n",
    "    hadamard_transform(x)\n",
    "    simon_qfunc_with_bipartite_s(PARTITION_INDEX, x, res)\n",
    "\n",
    "\n",
    "qmod = create_model(main, constraints=Constraints(optimization_parameter=\"width\"))\n",
    "\n",
    "# synthesize\n",
    "qprog = synthesize(qmod)\n",
    "\n",
    "# vizualize\n",
    "show(qprog)\n",
    "\n",
    "# execute\n",
    "result = execute(qprog).result()\n",
    "\n",
    "# plot the f(x)\n",
    "my_result = {\n",
    "    sample.state[\"x\"]: sample.state[\"res\"] for sample in result[0].value.parsed_counts\n",
    "}\n",
    "fig, ax = plt.subplots()\n",
    "ax.plot(my_result.keys(), my_result.values(), \"o\")\n",
    "ax.grid(axis=\"y\", which=\"minor\")\n",
    "ax.grid(axis=\"y\", which=\"major\")\n",
    "ax.grid(axis=\"x\", which=\"minor\")\n",
    "ax.grid(axis=\"x\", which=\"major\")\n",
    "plt.xlabel(\"$x$\", fontsize=16)\n",
    "plt.ylabel(\"$f(x)$\", fontsize=16)\n",
    "plt.yticks(fontsize=16)\n",
    "plt.xticks(fontsize=16)\n",
    "ax.minorticks_on()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e0457d6-f0a0-4c11-afba-394ff2733203",
   "metadata": {},
   "source": [
    "### Running the Simon's Algorithm\n",
    "\n",
    "As in the first example, we take $50*N$ shots."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "d1bbd003-27fc-4d1e-9ecc-f7cbdd512831",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:28.927858Z",
     "iopub.status.busy": "2024-05-07T14:48:28.926609Z",
     "iopub.status.idle": "2024-05-07T14:48:28.973762Z",
     "shell.execute_reply": "2024-05-07T14:48:28.972878Z"
    }
   },
   "outputs": [],
   "source": [
    "@qfunc\n",
    "def main(x: Output[QNum]):\n",
    "\n",
    "    allocate(NUM_QUBITS, x)\n",
    "    simon_qfunc(lambda x, res: simon_qfunc_with_bipartite_s(PARTITION_INDEX, x, res), x)\n",
    "\n",
    "\n",
    "qmod = create_model(\n",
    "    main, execution_preferences=ExecutionPreferences(num_shots=50 * NUM_QUBITS)\n",
    ")\n",
    "\n",
    "write_qmod(qmod, \"simon_shallow_example\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4297c903-f4f8-436b-a62f-755b037012b9",
   "metadata": {},
   "source": [
    "We synthesize and execute to obtain the results "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "2b893be2-6d70-4264-a84b-38ec318f83c8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:28.979004Z",
     "iopub.status.busy": "2024-05-07T14:48:28.977800Z",
     "iopub.status.idle": "2024-05-07T14:48:32.516515Z",
     "shell.execute_reply": "2024-05-07T14:48:32.515714Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/af819a46-e820-471f-9c62-65af85eccd18?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "qprog = synthesize(qmod)\n",
    "show(qprog)\n",
    "result = execute(qprog).result()\n",
    "samples = [\n",
    "    [int(k) for k in key] for key in result[0].value.counts_of_output(\"x\").keys()\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "a5ef7dbb-a01c-401a-9c16-d9d9d17f9a6c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:32.521320Z",
     "iopub.status.busy": "2024-05-07T14:48:32.520096Z",
     "iopub.status.idle": "2024-05-07T14:48:32.536778Z",
     "shell.execute_reply": "2024-05-07T14:48:32.536046Z"
    }
   },
   "outputs": [],
   "source": [
    "matrix_of_ind_v = get_independent_set(samples)\n",
    "assert (\n",
    "    len(matrix_of_ind_v) == NUM_QUBITS - 1\n",
    "), \"Failed to find an independent set, try to increase the number of shots\"\n",
    "quantum_secret_integer = get_secret_integer(matrix_of_ind_v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "36311726-fb1d-40d7-b830-ab4af4e2ea84",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:48:32.541426Z",
     "iopub.status.busy": "2024-05-07T14:48:32.540171Z",
     "iopub.status.idle": "2024-05-07T14:48:32.547594Z",
     "shell.execute_reply": "2024-05-07T14:48:32.546952Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The secret binary string (integer) of f(x): 60\n",
      "The result of the Simon's Algorithm: 60\n"
     ]
    }
   ],
   "source": [
    "s_secret = int(\"1\" * PARTITION_INDEX + \"0\" * (NUM_QUBITS - PARTITION_INDEX), 2)\n",
    "print(\"The secret binary string (integer) of f(x):\", s_secret)\n",
    "print(\"The result of the Simon's Algorithm:\", quantum_secret_integer)\n",
    "assert (\n",
    "    s_secret == quantum_secret_integer\n",
    "), \"The Simon's algorithm failed to find the secret key.\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29ddb559-94c2-48eb-87ca-63a95d8d7a16",
   "metadata": {},
   "source": [
    "## Technical Notes\n",
    "\n",
    "Below we provide some technical details about the quantum and classical parts of the Simon's algorithm"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a0b7c46-89db-4f0e-8dac-df1529525ec6",
   "metadata": {},
   "source": [
    "### The Quantum Part\n",
    "\n",
    "Following the three blocks of the algorithm:\n",
    "$$\n",
    "|0\\rangle_N |0\\rangle_N \\rightarrow \\frac{1}{2^{N/2}}\\sum^{2^N-1}_{x=0}|x\\rangle_N |0\\rangle_N  \\rightarrow \\frac{1}{2^{N/2}}\\sum^{2^N-1}_{x=0}|x\\rangle_N |f(x)\\rangle_N \n",
    " \\rightarrow \\frac{1}{2^{N}} \\sum^{2^N-1}_{y=0} |y\\rangle_N \\left( \\sum^{2^N-1}_{x=0}(-1)^{x\\cdot y} |f(x)\\rangle_N \\right),\n",
    "$$\n",
    "and we measure the first register $|y\\rangle$. First, we treat the case that $f(x)$ is a 2-to-1 function. The claim is that for any measured state $y$, we must have that $y\\cdot s=0$. To see this, calculate the probability of measuring some state $|y\\rangle$:\n",
    "$$\n",
    "P(|y\\rangle) \\propto \\left| \\sum^{2^N-1}_{x=0}(-1)^{x\\cdot y} |f(x)\\rangle_N \\right|^2.\n",
    "$$\n",
    "Now, change the sum to run over the image of $f(x)$ instead of over all $x\\in [0,2^{N}-1]$. Since for any $f(x)$ there are two sources in the domain, $x$ and $x\\oplus s$,  we can write\n",
    "$$\n",
    "P(|y\\rangle) \\propto \\left| \\sum_{f(x) \\in imag(f)} \\left[ (-1)^{x\\cdot y} + (-1)^{(x\\oplus s )\\cdot y} \\right]|f(x)\\rangle_N \\right|^2.\n",
    "$$\n",
    "Finally, if we assume by negation that  $y\\cdot s =1$ then the above expression is evaluted to zero, and we get a contradiction that $y$ was measured.\n",
    "Hence, for any measured $y$ we have $y\\cdot s =0$.\n",
    "\n",
    "If $s=0^N$, we still measure a set of $N-1$ independent $y$ with high probability (each $y$ with $1/2^N$ probability)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fed2c380-c13c-4f17-b6e3-4a41d7eeadc0",
   "metadata": {},
   "source": [
    "### The Classical Part\n",
    "\n",
    "We have a set of possible $y$ values that can be measured, each with the same probability of $1/M$, where $M$ is the set size. In the case that $s=0^N$ we have $M=2^N$, whereas for $s\\neq 0^N$ the set size is $M=2^{N-1}$. The probability to measure a set of $N-1$ linearly independent binary strings $y$ can be calculated as follows (see also the Birthday problem [[2](#BDWiki)]): For the first string we just require that we do not pick $y=0^N$, so $P(y_0)=1-1/M$. Then, for the next string, we require that it is not in $\\left\\{a_0 y_0\\,\\,\\,| a_0=0,1\\right\\}$, thus $P(y_1)=(1-2/M)$. The following string is required not to be picked out of $\\left\\{a_0 y_0+a_1y_1\\,\\,\\,| a_0, a_1=0,1\\right\\}$. We can continue with this procedure up to $y_{N-1}$ to get\n",
    "$$\n",
    "P_{\\rm independent} = \n",
    "\\left\\{\\begin{array}{l l}\n",
    "\\Pi^{2^{N-2}}_{k=0} \\left(1-2^k/2^{N}\\right) & \\text{,  if } f(x) \\text{ is 1-to-1},\\\\\n",
    "\\Pi^{2^{N-2}}_{k=0} \\left(1-2^k/2^{N-1}\\right) & \\text{,   if } f(x) \\text{ is 2-to-1}\n",
    "\\end{array}\n",
    "\\,\\,\\,\\,\\,\n",
    "\\geq \\Pi^{\\infty}_{k=1}\\left(1-\\frac{1}{2^k}\\right) \\approx 0.2887 \\geq 1/4.\n",
    "\\right.\n",
    "$$\n",
    "If we repeat the experiment $K$ times then the probability to measure an independent set improves exponentially."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e99222a4-d11b-46cd-ae44-5baa602d1567",
   "metadata": {},
   "source": [
    "## References\n",
    "\n",
    "<a id='SimonsWiki'>[1]</a>: [Simon's algorithm (Wikipedia)](https://en.wikipedia.org/wiki/Simon%27s_problem)\n",
    "\n",
    "<a id='BDWiki'>[2]</a>: [Birthday problem (Wikipedia)](https://https://en.wikipedia.org/wiki/Birthday_problem)\n",
    "\n",
    "<a id='SimonsPaper2024'>[3]</a>: [Singkanipa P., et al. \"Demonstration of Algorithmic Quantum Speedup for an Abelian Hidden Subgroup Problem.\" arXiv preprint arXiv:2401.07934 (2024).](https://arxiv.org/abs/2401.07934)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
